#ifndef __LOGGER_HPP__
#define __LOGGER_HPP__

#ifndef _GNU_SOURCE

#define _GNU_SOURCE

#endif
#include "Common.hpp"
#include "Sink.hpp"
#include "Message.hpp"
#include "Formatter.hpp"
#include "Level.hpp"
#include "Buffer.hpp"
#include "Lopper.hpp"
#include <mutex>
#include <atomic>
#include <cstdarg>
#include <unordered_map>

namespace MyLog
{
    class Logger
    {
    public:
        Logger(const std::string &loggername,const Formatter::ptr &formatter,
                const std::vector<LogSink::LogSinkshptr> &sink,
                const LogLevel::Level &level=LogLevel::Level::DEBUG)
            :_logger_name(loggername),
             _formatter(formatter),
             _sinks(sink),
             _output_limit_level(level)
        {}
        using ptr = std::shared_ptr<Logger>; 
        const std::string &GetLoggerName(){return _logger_name;}
        void debug(const std::string &file,std::string line,const std::string &fmt,...)
        {
            //先判断当前日志等级是否大于默认输出等级
            if(LogLevel::Level::DEBUG < _output_limit_level)return;
            //对fmt和不定参数进行解析，得到日志消息的字符串
            va_list ap;
            va_start(ap,fmt);
            char* res;
            int ret = vasprintf(&res,fmt.c_str(),ap);
            if(ret==-1)
            {
                PrintError("vasprintf failed!");
                return;
            }
            va_end(ap);
            //封装逻辑
            Encapulation(LogLevel::Level::DEBUG,file,line,res);
            free(res);
        }
        void info(const std::string &file,std::string line,const std::string &fmt,...)
        {
            //先判断当前日志等级是否大于默认输出等级
            if(LogLevel::Level::INFO < _output_limit_level)return;
            //对fmt和不定参数进行解析，得到日志消息的字符串
            va_list ap;
            va_start(ap,fmt);
            char* res;
            int ret = vasprintf(&res,fmt.c_str(),ap);
            if(ret==-1)
            {
                PrintError("vasprintf failed!");
                return;
            }
            va_end(ap);
            // std::cout<<"hello1\n";
            //封装逻辑
            // std::cout<<"hello\n";
            Encapulation(LogLevel::Level::INFO,file,line,res);
            free(res);
        }
        void warning(const std::string &file,std::string line,const std::string &fmt,...)
        {
            //先判断当前日志等级是否大于默认输出等级
            if(LogLevel::Level::WARNING < _output_limit_level)return;
            //对fmt和不定参数进行解析，得到日志消息的字符串
            va_list ap;
            va_start(ap,fmt);
            char* res;
            int ret = vasprintf(&res,fmt.c_str(),ap);
            if(ret==-1)
            {
                PrintError("vasprintf failed!");
                return;
            }
            va_end(ap);
            //封装逻辑
            Encapulation(LogLevel::Level::WARNING,file,line,res);
            free(res);
        }
        void error(const std::string &file,std::string line,const std::string &fmt,...)
        {
            //先判断当前日志等级是否大于默认输出等级
            if(LogLevel::Level::ERROR < _output_limit_level)return;
            //对fmt和不定参数进行解析，得到日志消息的字符串
            va_list ap;
            va_start(ap,fmt);
            char* res;
            int ret = vasprintf(&res,fmt.c_str(),ap);
            if(ret==-1)
            {
                PrintError("vasprintf failed!");
                return;
            }
            va_end(ap);
            //封装逻辑
            Encapulation(LogLevel::Level::ERROR,file,line,res);
            free(res);
        }
        void fatal(const std::string &file,std::string line,const std::string &fmt,...)
        {
            //先判断当前日志等级是否大于默认输出等级
            if(LogLevel::Level::FATAL < _output_limit_level)return;
            //对fmt和不定参数进行解析，得到日志消息的字符串
            va_list ap;
            va_start(ap,fmt);
            char* res;
            int ret = vasprintf(&res,fmt.c_str(),ap);
            if(ret==-1)
            {
                PrintError("vasprintf failed!");
                return;
            }
            va_end(ap);
            //封装逻辑
            Encapulation(LogLevel::Level::FATAL,file,line,res);
            free(res);
        }
    protected:
        virtual void log(const char *data, const std::size_t len)=0;
    private:
        void Encapulation(const LogLevel::Level &level,const std::string &file,
            std::string line,const std::string &fmt)
        {
            //构建LogMessage对象
            LogMessage fmtobj(level,file,line,fmt,_logger_name);
            //通过格式化工具对fmt进行格式化，得到格式化后的日志字符串
            std::stringstream ss;
            _formatter->format(ss,fmtobj);
            // std::cout<<"hello2\n";
            //进行日志落地
            log(ss.str().c_str(),ss.str().size());
        }
    protected:
        std::string _logger_name;
        std::mutex _mutex;
        std::atomic<LogLevel::Level> _output_limit_level;
        Formatter::ptr _formatter;
        std::vector<LogSink::LogSinkshptr> _sinks;
    };

    class SyncLogger : public Logger
    {
    public:
        SyncLogger(const std::string &loggername,const Formatter::ptr formatter,
                const std::vector<LogSink::LogSinkshptr> &sink,
                const LogLevel::Level &level=LogLevel::Level::DEBUG)
            :Logger(loggername,formatter,sink,level)
        {}
        void log(const char *data, const std::size_t len) override
        {
            // std::cout<<"hello4\n";
            std::unique_lock<std::mutex> lock(_mutex);
            if(_sinks.empty())return;
            for(auto &item :_sinks)
            {               
                item->log(data,len);
            }
        }
    };

    class AsyncLogger : public Logger
    {
    public:
        AsyncLogger(const std::string &loggername,const Formatter::ptr formatter,
                const AsyncType &loppertype,
                const std::vector<LogSink::LogSinkshptr> &sink,
                const LogLevel::Level &level=LogLevel::Level::DEBUG)
            :Logger(loggername,formatter,sink,level),
            //  _loppertype(loppertype)
             _loppertype(std::make_shared<AsyncLooper>(std::bind(&AsyncLogger::reallog,this,std::placeholders::_1),loppertype))
        {}
        //由异步线程把日志写入消费者缓冲区
        void log(const char *data, const std::size_t len) override
        {
            _loppertype->Push(data,len);
        }
        // 日志实际的落地方向
        void reallog(Buffer &buffer)
        {
            if(_sinks.empty())return;
            for(auto &item :_sinks)
            {               
                item->log(buffer.begin(),buffer.readAbleSize());
            }
        }

    private:
        AsyncLooper::Asyncshptr _loppertype;
    };

    enum class LoggerType
    {
        LOGGER_SYNC=0,
        LOGGER_ASYNC
    };
    //每次进行构建日志器时时，都需要为函数调用创建多个对象并传递参数，
    //初次使用还需要了解每个类和及其属性是做什么的，增加了用户使用成本
    //所以我们这里使用使用建造者模式，将构建日志器的过程封装起来，以便用户可以直接使用
    //1.抽象一个日志建造者类(完成日志器对象所需零部件的构建&日志器的构建)
    //  1.设置日志器类型(同步日志器，异步日志器)
    //  2.将不同的日志器的构造放在同一个日志建造者类中完成
    class LoggerBuilder
    {
    public:
        LoggerBuilder()
                :_loggertype(LoggerType::LOGGER_SYNC),
                 _output_limit_level(LogLevel::Level::DEBUG),
                 _loppertype(AsyncType::ASYNC_SAFE)
        {}
        using ptr = std::shared_ptr<LoggerBuilder>;
        void buildEnableUnsafe(){_loppertype=AsyncType::ASYNC_UNSAFE;}
        void buildLogType(const LoggerType &logtype){_loggertype=logtype;}
        void buildLoggerName(const std::string &loggername){_logger_name=loggername;}
        void buildLogLevel(const LogLevel::Level &level){_output_limit_level=level;}
        void buildFormatter(const std::string &pattern)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }

        template <typename SinkType,typename ...Args>
        void buildLogSink(Args ...args)
        {
            LogSink::LogSinkshptr sinks=SinkFactory::CreateSink<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(sinks);
        }
        virtual Logger::ptr build()=0;
    protected:
        AsyncType _loppertype;
        std::string _logger_name;
        LoggerType _loggertype;
        LogLevel::Level _output_limit_level;
        Formatter::ptr _formatter;
        std::vector<LogSink::LogSinkshptr> _sinks;//子类中调用纯虚函数build后记得push到容器里
    };
    //2.派生出具体的建造者类--局部日志器的建造者 & 全局的日志器建造者(后边添加了全局单例管理器后，将日志添加全局管理)
    
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        Logger::ptr build() override
        {
            assert(!_logger_name.empty());
            if(_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if(_sinks.empty())
            {
                buildLogSink<StdOutSink>();
            }
            if(_loggertype == LoggerType::LOGGER_ASYNC)
            {
                return std::make_shared<AsyncLogger>(_logger_name,_formatter,_loppertype,_sinks,_output_limit_level);
            }
            return std::make_shared<SyncLogger>(_logger_name,_formatter,_sinks,_output_limit_level);
        }
    };

    class LoggerManager
    {
    public:
        static LoggerManager &GetInstance()
        {
            // c++11后是线程安全的
            static LoggerManager logger;
            return logger;
        }
        void addLogger(const Logger::ptr &logger)
        {
            if(hasLogger(logger))return ;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.insert(std::make_pair(logger->GetLoggerName(),logger));
        }
        bool hasLogger(const Logger::ptr &logger)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if(_loggers.find(logger->GetLoggerName())==_loggers.end())
                return false;
            return true;
        }
        Logger::ptr GetLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it=_loggers.find(name);
            if(it==_loggers.end())
                return Logger::ptr();
            return it->second; 
        }
        Logger::ptr DefaultLogger(){return _DefaultLogger;}
    private:
        LoggerManager()
        {
            // std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());
            std::unique_ptr<LoggerBuilder> builder(new LocalLoggerBuilder());

            builder->buildLoggerName("DefalultLogger");
            _DefaultLogger =builder->build();
            _loggers.insert(std::make_pair("DefalultLogger",_DefaultLogger));
        }
        LoggerManager(const LoggerManager&)=delete;
        LoggerManager &operator=(const LoggerManager&)=delete;
    private:
        std::mutex _mutex;
        Logger::ptr _DefaultLogger;
        std::unordered_map<std::string,Logger::ptr> _loggers;
    };

    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
    using ptr = std::shared_ptr<GlobalLoggerBuilder>;
        Logger::ptr build() override
        {
            assert(!_logger_name.empty());
            if(_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            if(_sinks.empty())
            {
                buildLogSink<StdOutSink>();
            }
            Logger::ptr logger;
            if(_loggertype == LoggerType::LOGGER_ASYNC)
            {
                logger = std::make_shared<AsyncLogger>(_logger_name,_formatter,_loppertype,_sinks,_output_limit_level);
            }
            else
            {
                logger = std::make_shared<SyncLogger>(_logger_name,_formatter,_sinks,_output_limit_level);
            }
            LoggerManager::GetInstance().addLogger(logger);
            return logger;
        }
    };

}

#endif